package services

import (
	"gitee.com/zhoulvvv/my_go_gateway/dao"
	"gitee.com/zhoulvvv/my_go_gateway/dto"
	"gitee.com/zhoulvvv/my_go_gateway/public"
	"github.com/e421083458/golang_common/lib"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"log"
	"net/http/httptest"
	"sync"
	"time"
)

type AppService struct {
	tx *gorm.DB
}

func NewAppService() (*AppService, error) {
	tx, err := lib.GetGormPool("default")
	if err != nil {
		return nil, errors.New("获取数据库连接池失败")
	}
	return &AppService{
		tx: tx,
	}, nil
}

var (
	AppManagerHandler *AppManager
)

type AppManager struct {
	AppMap   map[string]*dao.App
	AppSlice []*dao.App
	Locker   sync.RWMutex
	init     sync.Once
	err      error
}

func init() {
	AppManagerHandler = NewAppManager()
}

func NewAppManager() *AppManager {
	return &AppManager{
		AppMap:   map[string]*dao.App{},
		AppSlice: []*dao.App{},
		Locker:   sync.RWMutex{},
		init:     sync.Once{},
	}
}

// 加载租户信息到内存中
func (a *AppManager) LoadOnce() error {
	a.init.Do(func() {
		tx, err := lib.GetGormPool("default")
		if err != nil {
			a.err = errors.New("获取数据库连接池失败")
			return
		}
		c, _ := gin.CreateTestContext(httptest.NewRecorder())
		appInfo := &dao.App{}
		param := &dto.AppListInput{PageNo: 1, PageSize: 999999}
		offset := (param.PageNo - 1) * param.PageSize
		param.PageNo = offset
		appList, _, err := appInfo.AppList(c, tx, param)
		if err != nil {
			a.err = errors.New("加载App租户信息失败：" + err.Error())
			return
		}
		a.Locker.Lock()
		defer a.Locker.Unlock()
		for _, item := range appList {
			a.AppMap[item.AppId] = &item
			a.AppSlice = append(a.AppSlice, &item)
		}
	})
	return a.err
}

func (a *AppManager) GetAppList() []*dao.App {
	return a.AppSlice
}

func (a *AppService) GetAppList(c *gin.Context, param *dto.AppListInput) (*dto.AppListOutput, error) {
	param.PageNo = (param.PageNo - 1) * param.PageSize
	appModel := &dao.App{}
	list, total, err := appModel.AppList(c, a.tx, param)
	if err != nil {
		return nil, err
	}
	outputItemList := []dto.AppListItemOutput{}
	// 租户统计器
	for _, item := range list {
		appCounter, err := public.FlowCounterHandler.GetCounter(public.FlowAppPrefix + item.AppId)
		if err != nil {
			return nil, errors.New("获取租户流量统计器失败：" + err.Error())
		}
		realQps := appCounter.QPS
		realQpd := appCounter.TotalCount
		outputItemList = append(outputItemList, dto.AppListItemOutput{
			ID:       int64(item.Id),
			AppID:    item.AppId,
			Name:     item.Name,
			Secret:   item.Secret,
			WhiteIPS: item.WhiteIps,
			Qpd:      int64(item.Qpd),
			Qps:      int64(item.Qps),
			RealQpd:  realQpd,
			RealQps:  realQps,
		})
	}

	return &dto.AppListOutput{
		Total: total,
		List:  outputItemList,
	}, nil
}

func (a *AppService) GetAppDetail(c *gin.Context, param *dto.AppDetailInput) (*dao.App, error) {
	search := &dao.App{
		Id: param.Id,
	}
	detail, err := search.Find(c, a.tx, search)
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, errors.New("获取租户详情失败：" + err.Error())
	}
	return detail, nil
}

func (a *AppService) AppDelete(c *gin.Context, param *dto.AppDetailInput) error {
	search := &dao.App{
		Id: param.Id,
	}
	detail, err := search.Find(c, a.tx, search)
	if err == gorm.ErrRecordNotFound {
		return errors.New("该租户不存在")
	}
	if err != nil {
		return errors.New("删除租户时获取租户详情失败：" + err.Error())
	}
	detail.IsDelete = 1
	return detail.Save(c, a.tx)
}

func (a *AppService) AppAdd(c *gin.Context, param *dto.AppAddInput) error {
	// 验证AppId是否被占用
	search := &dao.App{
		AppId: param.AppID,
	}

	if _, err := search.Find(c, a.tx, search); err != nil && err != gorm.ErrRecordNotFound {
		return errors.New("租户Id被占用，请重写输入")
	}
	// 判断秘钥是否填写，如果没有，就自动生成
	if param.Secret == "" {
		param.Secret = public.MD5(param.AppID)
	}
	appModel := &dao.App{
		AppId:    param.AppID,
		Name:     param.Name,
		Secret:   param.Secret,
		WhiteIps: param.WhiteIPS,
		Qpd:      int(param.Qpd),
		Qps:      int(param.Qps),
		IsDelete: 0,
	}
	return appModel.Save(c, a.tx)
}

func (a *AppService) AppUpdate(c *gin.Context, param *dto.AppUpdateInput) error {
	search := &dao.App{
		Id: int(param.Id),
	}
	appModel, err := search.Find(c, a.tx, search)
	if err == gorm.ErrRecordNotFound {
		return errors.New("该租户不存在")
	}
	if err != nil {
		return errors.New("修改租户时，查询租户出错：" + err.Error())
	}
	// 判断秘钥是否填写，如果没有，就自动生成
	if param.Secret == "" {
		param.Secret = public.MD5(param.AppID)
	}
	appModel.Name = param.Name
	appModel.Secret = param.Secret
	appModel.WhiteIps = param.WhiteIPS
	appModel.Qpd = int(param.Qpd)
	appModel.Qps = int(param.Qps)
	return appModel.Save(c, a.tx)
}

func (a *AppService) AppStat(c *gin.Context, param *dto.AppDetailInput) (*dto.StatisticsOutput, error) {
	search := &dao.App{
		Id: param.Id,
	}
	detail, err := search.Find(c, a.tx, search)
	if err == gorm.ErrRecordNotFound {
		return nil, errors.New("该租户不存在")
	}
	if err != nil {
		return nil, errors.New("获取租户统计时，查询租户出错：" + err.Error())
	}
	// 进入流量统计
	appCounter, err := public.FlowCounterHandler.GetCounter(public.FlowAppPrefix + detail.AppId)
	if err != nil {
		return nil, errors.New("获取租户流量统计器失败：" + err.Error())
	}
	todayStat := []int64{}
	currentTime := time.Now()
	for i := 0; i <= currentTime.In(lib.TimeLocation).Hour(); i++ {
		newTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), i, 0, 0, 0, lib.TimeLocation)
		// 获取当前小时的QPS
		hourData, err := appCounter.GetHourData(newTime)
		if err != nil {
			log.Printf("获取 %s 租户当天 %s 小时的QPS数据出现错误：%s\n", detail.Name, newTime.Format("2006010215"), err.Error())
		}
		todayStat = append(todayStat, hourData)
	}
	yesterdayStat := []int64{}
	// 获取昨天的时间
	yesterTime := currentTime.Add(-1 * time.Duration(time.Hour*24))
	for i := 0; i <= 23; i++ {
		newTime := time.Date(yesterTime.Year(), yesterTime.Month(), yesterTime.Day(), i, 0, 0, 0, lib.TimeLocation)
		// 获取当前小时的QPS
		yesterData, err := appCounter.GetHourData(newTime)
		if err != nil {
			log.Printf("获取 %s 租户昨天 %s 小时的QPS数据出现错误：%s\n", detail.Name, newTime.Format("2006010215"), err.Error())
		}
		yesterdayStat = append(yesterdayStat, yesterData)
	}
	return &dto.StatisticsOutput{
		Today:     todayStat,
		Yesterday: yesterdayStat,
	}, nil
}
